/************************************************************
*
* DS_STRID.CPP
* 
*
*************************************************************/
#include <ap/settings/ds_settings.h>

#include "ap/ap_comm.h"
#include "ap/ap_dlstat.h"
#include "ap/ap_prof_counters.h"
#include <m3d.h>
#include "ds_strid.h"
#include "ds_vector.h"
#include "ds_lf_map.h"
#include "ds_pool.h"
#include "ds_data.h"
#include "ds_data_type_impl.hpp"

#include "ds_map.h"
#include "ds_param.h"
#include "ds_event_mgr.h"

#include <fileio.h>

const int MAX_STRING_BLOCKS = 256;
const int STRING_BLOCK_SIZE = 8192;



namespace ds_data {
   template<>
   static BOOL Convert<dsSTRING, dsSTRID>(const dsSTRING &src, dsSTRID &dest) {
      dest = dsSTRID(src,false);
      return TRUE;
   }
   static ConvertRegistrator<dsSTRING, dsSTRID> regCvtdsSTRINGdsSTRID(DSDCF_ALL);

   template<>
   static BOOL Convert<dsSTRID, dsSTRING>(const dsSTRID &src, dsSTRING &dest) {
      dest = src.CStr();
      return TRUE;
   }
   static ConvertRegistrator<dsSTRID, dsSTRING> regCvtdsSTRIDdsSTRING(DSDCF_ALL);
}

///DEFINE_STD_CONVERTER(dsSTRING, dsSTRID, DSDCF_ALL)
DEFINE_MT_SAFE_TYPE_ANALOG(dsSTRING, dsSTRID);
DEFINE_STD_COMPARATOR(dsSTRID, dsSTRID)
DEFINE_TYPE_ID(dsSTRID)


class dsSTRID_POOL {
public:
   enum { EMPTY_ID = 0 };

                  dsSTRID_POOL         (void);
                  ~dsSTRID_POOL        (void);

   //BOOL           IsValidId            (int id) const;

   const char*    GetStrById           (int id) const { return stringTable[id]; }
   //const char*    GetStr_Dbg           (int id) const;      // for debug only

   dsSTRID        GetIdByStr           (const char *str) const;
   dsSTRID        AddStr               (const char *str);

protected:
   const char    *AllocStr             (const char *str); 

public:
   typedef dsLF_SORTED_MAP<const char*, dsSTRID, dsSTR_HASH, dsSTR_CMP> STRING_MAP;

   static void    RegisterThread       (void) { STRING_MAP::RegisterThread(); }
   static void    UnregisterThread     (void) { STRING_MAP::UnregisterThread(); }
   static void    UpdateThread         (void) { STRING_MAP::UpdateThread(); }

protected:
   STRING_MAP           stringMap;
   dsPOOL<const char*>  stringTable;                     // contain pointers to strings. since the storage is not reallocated they do not change
   int                  idxStorage, posStorage;
   char                 *strStorage[MAX_STRING_BLOCKS];   // storage for the strings. maximum 2M in 8k blocks
   apCL                 _cl;

   class CMP {
      const dsSTRID_POOL   *pPool;
   public:
               CMP   (const dsSTRID_POOL *d) : pPool(d) {}
      int      cmp   (const dsSTRID &a, const dsSTRID &b) const { return strcmp(a.CStr(), b.CStr()); }
      int      cmp   (const dsSTRID &a, const char *b) const    { return strcmp(a.CStr(), b); }
   };
  
   friend class CMP;
};

static dsSTRID_POOL *dsStrIdPool    = NULL;
static const char *dsStrIdOfEmptyId = NULL;


dsSTRID_POOL& GetStrIdPool(void)
{
#ifdef _AP_PS3
   if (!__builtin_expect((int)dsStrIdPool, 1)) 
#else
   if (!dsStrIdPool) 
#endif
   {
      dsStrIdPool = apNEW dsSTRID_POOL;
      dsStrIdPool->AddStr("");
      dsStrIdOfEmptyId = GetStrIdPool().GetStrById(dsSTRID_POOL::EMPTY_ID);
   }
   
   return *dsStrIdPool;
}

void  dsSTRID_RegisterThread     (void) { dsSTRID_POOL::RegisterThread(); }
void  dsSTRID_UnregisterThread   (void) { dsSTRID_POOL::UnregisterThread(); }
void  dsSTRID_UpdateThread       (void) { dsSTRID_POOL::UpdateThread(); }

//DECLARE_LF_SORTED_MAP_STATICS(dsSTRID_POOL::STRING_MAP)

//dsERASE_MANAGER<dsLF_SORTED_MAP<const char*, dsSTRID, STR_HASH, STR_CMP>::LINE_ERASER> dsLF_SORTED_MAP<const char*, dsSTRID, STR_HASH, STR_CMP>::eraseMgr;
//AP_DECLARE_IN_TLS int dsERASE_MANAGER<dsLF_SORTED_MAP<const char*, dsSTRID, STR_HASH, STR_CMP>::LINE_ERASER>::threadId;

//
// class dsSTRID
//
/***************************************************************
* dsSTRID::dsSTRID ()
*
***************************************************************/
dsSTRID::dsSTRID(void)
{
   id = dsStrIdOfEmptyId;
   return;
}

/***************************************************************
* dsSTRID::dsSTRID ()
*
***************************************************************/
dsSTRID::dsSTRID  (int _id)
{
   id = ds::Int2Ptr<char>(_id);
}

/***************************************************************
* dsSTRID::dsSTRID ()
*
***************************************************************/
dsSTRID::dsSTRID (const dsSTRID& _id, bool dummy)
{
   id = _id.id;
   return;
}

/***************************************************************
* dsSTRID::dsSTRID ()
*
***************************************************************/
dsSTRID::dsSTRID(const char *str, bool existOnly)
{
   if (str[0] == 0) {
      id = GetStrIdPool().GetStrById(dsSTRID_POOL::EMPTY_ID);
      return;
   }
   if (existOnly) {
      id = GetStrIdPool().GetIdByStr(str).id;
   } else {
      id = GetStrIdPool().AddStr(str).id;
   }
   return;
}

/***************************************************************
* dsSTRID::dsSTRID ()
*
***************************************************************/
dsSTRID::dsSTRID(const dsSTRING &str, bool existOnly)
{
   if (str.Length() == 0) {
      id = GetStrIdPool().GetStrById(dsSTRID_POOL::EMPTY_ID);
      return;
   }
   if (existOnly) {
      id = GetStrIdPool().GetIdByStr(str.CStr()).id;
   } else {
      id = GetStrIdPool().AddStr(str.CStr()).id;
   }
   return;
}

/***************************************************************
* dsSTRID::dsSTRID ()
*
***************************************************************/
dsSTRID::dsSTRID(const char *_id, int dummy1, int dummy2)
{
   id = _id;
   return;
}

/***************************************************************
* dsSTRID::CStr ()
*
***************************************************************/
const char *dsSTRID::CStr() const
{
   return id;
}


/***************************************************************
* dsSTRID::CStr_Dbg ()
*
***************************************************************/
const char *dsSTRID::CStr_Dbg() const
{
   return id;
}

/***************************************************************
* dsSTRID::IsValid()       
*
**************************************************************
bool dsSTRID::IsValid (void) const
{
   if (id == dsSTRID_POOL::INVALID_ID) {
      return false;
   }

   if (!GetStrIdPool().IsValidId(id)) {
      return false;
   }

   return true;
}*/


/***************************************************************
* dsSTRID::operator ==()       
*
***************************************************************/
bool dsSTRID::operator ==(const char *str) const
{
   return strcmp(id, str) == 0;
}

/***************************************************************
* dsSTRID::operator !=()       
*
***************************************************************/
bool dsSTRID::operator !=(const char *str) const
{
   return strcmp(id, str) != 0;
}

/***************************************************************
* dsSTRID::operator ==()       
*
***************************************************************/
bool dsSTRID::operator ==(const dsSTRING &str) const
{
   return strcmp(id, str.CStr()) == 0;
}

/***************************************************************
* dsSTRID::operator !=()       
*
***************************************************************/
bool dsSTRID::operator !=(const dsSTRING &str) const
{
   return strcmp(id, str.CStr()) != 0;
}

/***************************************************************
* dsSTRID::IsValid()       
*
***************************************************************/
bool dsSTRID::IsValid ( void ) const
{
   return id != dsStrIdOfEmptyId;
}

/*************************************************************
'* dsSTRID::IsEmpty()
'* 
'* RET : true => void/invalid or ""
'*************************************************************/
bool dsSTRID::IsEmpty ( void ) const
{
   if (!IsValid()) {
      return true;
   }
   return *id == 0;
}

//
// class dsSTRID_POOL
//
dsSTRID_POOL *psStrPool = NULL;

/***************************************************************
* dsSTRID_POOL::dsSTRID_POOL()       
*
***************************************************************/
dsSTRID_POOL::dsSTRID_POOL(void) : stringMap(AP_CL), 
stringTable(AP_CL), _cl(AP_CL_1)
{
   dsSTRID_RegisterThread();

   idxStorage  = 0;
   posStorage  = 0;

   for (int i = 0; i < MAX_STRING_BLOCKS; i++) {
      strStorage[i] = NULL;
   }

   stringMap.Init(ds::FindPrime(4096));

   return;
}

dsSTRID_POOL::~dsSTRID_POOL(void)
{
   dsSTRID_UnregisterThread();

   return;
}

/*************************************************************
 * dsSTRID_POOL::IsValidId()
 * 
 ************************************************************
BOOL dsSTRID_POOL::IsValidId(int id) const
{
   return stringTable.IsIndexValid(id);
}*/

/***************************************************************
* dsSTRID_POOL::GetStr_Dbg()
*
**************************************************************
const char *dsSTRID_POOL::GetStr_Dbg(int id) const
{
   return stringTable[id];
}*/

/***************************************************************
* dsSTRID_POOL::GetIdByStr()       
*
***************************************************************/
dsSTRID dsSTRID_POOL::GetIdByStr(const char *str) const
{
   const dsSTRID *strId = stringMap.Find(str);

   if (strId == NULL) {
      return dsSTRID();
   }

   return *strId;
}

/***************************************************************
* dsSTRID_POOL::AddStr()       
*
***************************************************************/
dsSTRID dsSTRID_POOL::AddStr(const char *str)
{
   const dsSTRID *strId = stringMap.Find(str);

   if (strId != NULL) {
      return *strId;
   }

   static osLOCK dsStrIdLock("dsStrIdLock");
   osLOCKER locker(dsStrIdLock);

   strId = stringMap.Find(str); // we have to repeat search in locked state to ensure the string wasn't added by another thread yet

   if (strId != NULL) {
      return *strId;
   }

   const char *s = AllocStr(str);
   int id = stringTable.Alloc(s);

   dsSTRID newStrId(stringTable[id], 1, 2);
   stringMap.Insert(s, newStrId);   // the memory barrier is inside, so we don't need add another one.
                                    // the string will become available to other threads as soon as hash entry is written

   return newStrId;
}

/*************************************************************
* *dsSTRID_POOL::AllocStr()
* 
*************************************************************/
const char *dsSTRID_POOL::AllocStr(const char *str)
{
   int len = strlen(str);

   STRONG_ASSERT(len + 1 < STRING_BLOCK_SIZE);

   if (posStorage + len + 1 > STRING_BLOCK_SIZE) {
      idxStorage ++;
      posStorage = 0;

      STRONG_ASSERT(idxStorage < _NElem(strStorage));
   }

   if (strStorage[idxStorage] == NULL) {
      strStorage[idxStorage] = (char*)dlMalloc(STRING_BLOCK_SIZE, _cl.file, _cl.line);
      posStorage = NULL;
   }

   const char *s = strStorage[idxStorage] + posStorage;
   memcpy(strStorage[idxStorage] + posStorage, str, len + 1);
   posStorage += len + 1;

   return s;
}

//
// End-of-file DS_STRID.CPP
//
